import EventEmitter from 'events'

import IOController from '../io/io-controller'

class TransmuxingController {
    constructor(mediaDataSource, config) {
        this._emitter = new EventEmitter()
        this._config = config

        // treat single part media as multipart media,
        // which has only one segment
        if (!mediaDataSource.segments) {
            mediaDataSource.segments = [{
                duration: mediaDataSource.duration,
                filesize: mediaDataSource.filesize,
                url: mediaDataSource.url
            }]
        }

        // fill in default IO params if not exists
        if (typeof mediaDataSource.cors !== 'boolean') {
            mediaDataSource.cors = true
        }
        if (typeof mediaDataSource.withCredentials !== 'boolean') {
            mediaDataSource.withCredentials = false
        }

        this._mediaDataSource = mediaDataSource
        this._currentSegmentIndex = 0
        let totalDutation = 0

        this._mediaDataSource.segments.forEach((segment) => {
            // timestampBase for each segment, and calculate total duration
            segment.timestampBase = totalDutation
            totalDutation += segment.duration
            // params needed by IOController
            segment.cors = mediaDataSource.cors
            segment.withCredentials = mediaDataSource.withCredentials
            // referrer policy control, if exist
            if (config.referrerPolicy) {
                segment.referrerPolicy = config.referrerPolicy
            }
        })

        if (
            !isNaN(totalDutation) &&
            this._mediaDataSource.duration !== totalDutation
        ) {
            this._mediaDataSource.duration = totalDutation
        }

        this._mediaInfo = null
        this._demuxer = null
        this._remuxer = null
        this._ioctl = null

        this._pendingSeekTime = null
        this._pendingResolveSeekPoint = null

        this._statisticsReporter = null
    }

    destroy() {

    }

    on(event, listener) {
        this._emitter.addListener(event, listener);
    }

    off(event, listener) {
        this._emitter.removeListener(event, listener);
    }

    start() {
        this._loadSegment(0)
        this._enableStatisticsReporter()
    }

    _enableStatisticsReporter() {
        if (this._statisticsReporter == null) {
            this._statisticsReporter = self.setInterval(
                this._reportStatisticsInfo.bind(this),
                this._config.statisticsInfoReportInterval
            )
        }
    }

    _reportStatisticsInfo() {
        let info = {}

        info.url = this._ioctl.currentURL
        info.hasRedirect = this._ioctl.hasRedirect
        if (info.hasRedirect) {
            info.redirectedURL = this._ioctl.currentRedirectedURL
        }

        info.speed = this._ioctl.currentSpeed
        info.loaderType = this._ioctl.loaderType
        info.currentSegmentIndex = this._currentSegmentIndex
        info.totalSegmentCount = this._mediaDataSource.segments.length

        this._emitter.emit(TransmuxingEvents.STATISTICS_INFO, info)
    }

    _loadSegment(segmentIndex, optionalFrom) {
        this._currentSegmentIndex = segmentIndex
        let dataSource = this._mediaDataSource.segments[segmentIndex]

        let ioctl = this._ioctl = new IOController(dataSource, this._config, segmentIndex)
        ioctl.onError = this._onIOException.bind(this)
        ioctl.onSeeked = this._onIOSeeked.bind(this)
        ioctl.onComplete = this._onIOComplete.bind(this)
        ioctl.onRedirect = this._onIORedirect.bind(this)
        ioctl.onRecoveredEarlyEof = this._onIORecoveredEarlyEof.bind(this)

        if (optionalFrom) {
            this._demuxer.bindDataSource(this._ioctl)
        } else {
            ioctl.onDataArrival = this._onInitChunkArrival.bind(this)
        }
        ioctl.open(optionalFrom)
    }

    _onInitChunkArrival(data, byteStart) {
        let probeData = null
        let consumed = 0

        if (byteStart > 0) {
            // IOController seeked immediately after opened, byteStart > 0 callback may received
            this._demuxer.bindDataSource(this._ioctl)
            this._demuxer.timestampBase = this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase

            consumed = this._demuxer.parseChunks(data, byteStart)
        } else if ((probeData = FLVDemuxer.probe(data)).match) {
            // Always create new FLVDemuxer
            this._demuxer = new FLVDemuxer(probeData, this._config)
        }
    }
}

export default TransmuxingController